/*
 * Copyright (C) 2012 by Hao Hou<ghost89413@gmail.com>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>*
 */

/*
 * This file contains function definations used to manipulate Input Contexts
 */
#ifndef __IC_H__
#define __IC_H__
#ifdef HAVE_CONFIG
#	include <config.h>
#endif
struct _InputContext;
#include <log.h>
#include <X11/Xlib.h>
#include <IMdkit.h>
#include <Xi18n.h>
#include <stdint.h>
#include <loader.h>
#include <pthread.h>
#include <UI.h>
#define MAX_IC_NUM 64
#define MAX_CAND   32
#define PREEDIT_BUF 1024
#define MAX_FORWARD_KEYS 64
/*
 * Attributes Defined by XIM Client
 */
typedef struct {
    XRectangle      Area;	
    XRectangle      AreaNeeded;	
    XPoint          SpotLocation;
    Colormap        StdColormap;
    CARD32          Foreground;	
    CARD32          Background;	
    Pixmap          BackgroundPixmap;
    char            FontSet[1024];	
    CARD32          LineSpace;	
    Cursor          Cursor;	
}PreeditAttribute;
typedef struct {
    XRectangle      Area;	
    XRectangle      AreaNeeded;	
    Colormap        StdColormap;
    CARD32          Foreground;	
    CARD32          Background;	
    Pixmap          BackgroundPixmap;	
    char            FontSet[1024];	
    CARD32          LineSpace;	
    Cursor          Cursor;	
}StatusAttribute;
/*
 * Datatype for represent candidates
 */
typedef struct {
	char*  Candidate[MAX_CAND];
	size_t Capacity;   //the maximum number of candidates
	size_t Count; 	   //the actual number of cacndidates
}CandidateList;
/*
 * Callback function and data type for User Event Forwarding mechemism.
 * The mechemism can override the event handler of Vine system.
 */
typedef int (*KeyForwardProc)(uint32_t sym, uint32_t state, uint32_t id,void* ic);
typedef struct {
	KeyForwardProc proc;	      //what should I do, when envet matches
	/*
	 * NOTE: In the foward filter, the mask filter have to be an descriptor of 
	 * 	 a condition in which the state will be selected.
	 * 	 Considering each bit, there are 3 kinds of assertions which we use 
	 * 	 to judge if state matches selector.
	 * 	 i) the bit MUST be 1
	 * 	ii) the bit MUST be 0
	 * iii) the bit CAN be ANY value
	 *       So for each bit, we have to assign 2 bits for it to represent the 
	 *       selector. That is why here we use two mask : negative mask and 
	 *       positive mask. 
	 *       if there is a state to be checked, the selector should check the 
	 *       bitwise AND between the state and the postive mask, if the result
	 *       is not the postive mask it self, it means that there is one or more
	 *       zero bits in state which the postive mask assert the bit should be 1.
	 *       Symmetericly, the bitwise AND between state and negative mask should
	 *       be 0, if the state can be selected.
	 *       In addition, an valid selector have to guarantee that the bitwise AND 
	 *       between negative and postive mask is 0. If the condition should not be
	 *       warrented, the selector should not accept any state. We will make sure
	 *       that user are not setting the selector like that :-)
	 *       This is a tricky solution, however to make a flexible framework, it seems
	 *       be a good choice......
	 */
	uint32_t sym_left, sym_right;   //sym should be in [sym_left,sym_right)
	uint32_t smask_pos, smask_neg;  //state filter
	uint32_t release;		//what kinds of events should be forward? press/release/any
	uint32_t enabled;		//you can disable/enable the forwarding any time you want
} ForwardEvent;
/*
 * Here comes the finally structure, the definition of Input Context
 */
typedef struct _InputContext{
	/* Because the same binary file should be load with different parameters,
	 * However dlopen does not allocate different memory for static variables
	 * when the library is reopen. 
	 * So the solution is to allocate memory for MetaData within the macro MetaDataBegin
	 * and define a macro that help the user IME to find out which IMMetaData dose 
	 * the IC depend on. So putting it at the beginning of the structure seems a 
	 * good solution, since the offset of the variable always be 0.
	 * for such reason,
	 * DO NOT MOVE THE POSITION OF CurrentIM, DO NOT ADD ANY DEFINITION BEFORE IT!
	 */
	IMMetaData*		CurrentIM;	       //Current active IM
	/* ID & Flags */
	CARD16			id; 		//Input Context ID(defined by XIM Protocol)
	CARD16			curent_connect; //Current actived connect ID
	CARD16			used;		//the IC is allocated
	
	/* Input Context Attributes(defined by XIM Protocol */
	int32_t		 	InputStyle;
	Window			ClientWindow;
	Window			FocusWindow;
	PreeditAttribute	pattr;
	StatusAttribute		sattr;	
	
	CARD16			Enabled;	//Is IME actived? WARNING: NEVER try to set it.

	/* Following members are related to an IME Instance */
	char			PreeditBuffer[1024];   //PreeditBuffer
	uint32_t		PreeditBufferLength;
	CandidateList	Candidates;	       //Used for UI to display suggestions
	char			PrecommitBuffer[1024]; //Used for UI to indiate what is being commit
	ForwardEvent	ForwardFilter[MAX_FORWARD_KEYS]; //User Event Forward
	void*			IMIC;//We allow each IM hold an user defined context
	
	/* Following members are UI Related*/
	char			TrayIcon[256];		//What icon(file name) should be used ?
	uint8_t			IMWindowVisable;	//Is recommendation window visible ?
	/* Mutex */
	pthread_mutex_t	mutex;
	pthread_t		CreatorThread;		//is function called from creator_thread ?
} InputContext;
/*
 * Create/Destory
 */
InputContext* ICNew();	//Allocate memory for an new IC
void ICDestory(InputContext* ic); //Finlaize the IC ,Despose the memory
void FinalizeIC(); //Finalize all IC when exit
/*
 * Find specified IC
 */
InputContext* FindIC(CARD16 id);  //Find IC by ICID
InputContext* GetCurrentIC(void* data); //Faster version of FindIC(data->icid)
InputContext* GetCurrentICByID(CARD16 id);//Faster version of FinddIC(id)
/*
 * Manipulate XIM defined attributes
 */
int ModifyIC(IMChangeICStruct* data); //Modify XIN defined IC attributes acccording XIM packets
int QueryIC(IMChangeICStruct* data);  //Query XIM defined IC attributes, result filled in *data*

/* Functions called by other */
int CreateIMIC(InputContext* ic, IMMetaData* im); //Create an user defined IC for specified IM 
int FreeIMIC(InputContext* ic);			  //Despose an user define IC
int UpdateCandidates(InputContext* ic, char** candidates, size_t n); //Update suggestions
int UpdateCommit(InputContext* ic, char* text); //update Precommit text
int SetForwardFilter(InputContext* ic, uint32_t sym_left, uint32_t sym_right,
					 uint32_t mask_pos, uint32_t mask_neg, uint32_t release,
					 KeyForwardProc proc);      //Set up event forward filters
//NOTE: If succeed, the function will return the index of ForwardFilter rather than 0
int SwitchForwardFilter(InputContext* ic, int index, int enable);  //want to disable filters?
KeyForwardProc GetForwardProc(InputContext* ic, uint32_t sym, uint32_t state, uint32_t release, uint32_t* id); 
int ResetForwardFilter(InputContext* ic);      //Remove all filters
int CommitText(InputContext* ic,char* text);   //Commit text to client window
//UI Releated
int RedrawIC(InputContext* ic);		       //Redraw UI for IC
int FollowCursorPosition(InputContext* ic);    //Make the IMWindow follow your cursor
/* Thread synchronization utils */
int LockIC(InputContext* ic);
int UnlockIC(InputContext* ic);
#endif
